﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Script.Serialization;
using log4net;
using System.Configuration;

namespace FlyComet
{
    public static class LiveMessageAggregator
    {
        public static Dictionary<String, List<String>> RequestedTopics = new Dictionary<String, List<String>>();
        public static Dictionary<String, DateTime> LastConnected = new Dictionary<string, DateTime>();
        public static Dictionary<String, List<JMSMessage>> Messages = new Dictionary<String, List<JMSMessage>>();
        static Object lockRequestObject = new object();
        static Object lockMessageObject = new object();
        private static readonly ILog log = LogManager.GetLogger(typeof(LiveMessageAggregator).Name);

        static System.Threading.Timer timer = null;
        const int OneMin = 60 * 1000;
        static LiveMessageAggregator()
        {
            timer = new System.Threading.Timer(timercallbck);
            timer.Change(OneMin * 10, OneMin * 10);
        }

        static void timercallbck(Object objj)
        {
            ClearUnusedRequests();
        }

        static public bool SubscribeTopic(String channel, String requestid)
        {
            try
            {
                lock (lockRequestObject)
                {
                    if (!RequestedTopics.ContainsKey(channel) || RequestedTopics[channel] == null)
                    {
                        RequestedTopics[channel] = new List<string>();
                    }
                    RequestedTopics[channel].Add(requestid);
                }
                LastConnected[requestid] = DateTime.Now;
                log.Info(String.Format("Subscribed for {0} from {1}", channel, requestid));
                return true;
            }
            catch (Exception ex)
            {
                log.Error(String.Format("Error subscribing request {0}, for channel {1}, error {2}", requestid, channel, ex.ToString()));
                return false;
            }
        }

        static public bool UnsubscribeTopic(String channel, String requestid)
        {
            try
            {
                lock (lockRequestObject)
                {
                    if (RequestedTopics.ContainsKey(channel))
                    {
                        if (RequestedTopics[channel] != null && RequestedTopics[channel].Contains(requestid))
                            RequestedTopics[channel].Remove(requestid);

                        if (RequestedTopics[channel].Count <= 0)
                            RequestedTopics.Remove(channel);
                    }
                }
                LastConnected[requestid] = DateTime.Now;
                log.Info(String.Format("Unsubscribed from {0} for {1}", channel, requestid));
                return true;
            }
            catch (Exception ex)
            {
                log.Error(String.Format("Error removing request {0}, for channel {1}, error {2}", requestid, channel, ex.ToString()));
                return false;
            }
        }

        private static void ClearUnusedRequests()
        {
            try
            {
                List<String> removablerequests = new List<string>();
                removablerequests.AddRange(LastConnected.Keys);
                foreach (String reqid in removablerequests)
                {
                    if (LastConnected[reqid].AddMinutes(45) < DateTime.Now)
                    {
                        RemoveRequestId(reqid);
                        LastConnected.Remove(reqid);
                    }
                }
                log.Error(String.Format("Cleared unused requests."));
            }
            catch (Exception ex)
            {
                log.Error(String.Format("Error Clearing unused requests {0}", ex.ToString()));
                return;
            }
        }

        static private bool RemoveRequestId(String reqid)
        {
            try
            {
                List<String> topicslist = new List<string>();
                topicslist.AddRange(RequestedTopics.Keys);
                foreach (String topicid in topicslist)
                {
                    lock (lockRequestObject)
                    {
                        if (RequestedTopics.ContainsKey(topicid))
                        {
                            if (RequestedTopics[topicid] != null && RequestedTopics[topicid].Contains(reqid))
                                RequestedTopics[topicid].Remove(reqid);

                            if (RequestedTopics[topicid].Count <= 0)
                                RequestedTopics.Remove(topicid);
                        }
                    }
                }

                return true;
            }
            catch (Exception ex)
            {
                log.Error(String.Format("Error removing request {0}, {1}",reqid, ex.ToString()));
                return false;
            }

        }

        static public List<JMSMessage> GetPendingMessages(String requestid)
        {
            
            List<JMSMessage> pendinglist = new List<JMSMessage>();
            if (Messages.ContainsKey(requestid) && Messages[requestid] != null)
            {
                pendinglist.AddRange(Messages[requestid]);
                LastConnected[requestid] = DateTime.Now;
            }
            lock (lockMessageObject)
            {
                Messages.Remove(requestid);
            }

            return pendinglist;
        }

        public static void PushMessage(string strmsg)
        {
            log.Debug(strmsg);
            if (String.IsNullOrEmpty(strmsg) || RequestedTopics.Count <= 0)
                return;
            try
            {
                String message = strmsg.Trim();
                JMSMessage jmsmsg = Newtonsoft.Json.JsonConvert.DeserializeObject<JMSMessage>(message);

                if (jmsmsg == null || String.IsNullOrEmpty(jmsmsg.Channel))
                    return;

                if (!RequestedTopics.ContainsKey(jmsmsg.Channel))
                    return;
                
                foreach (String requestid in RequestedTopics[jmsmsg.Channel])
                {
                    lock (lockMessageObject)
                    {
                        if (!Messages.ContainsKey(requestid) || Messages[requestid] == null)
                        {
                            Messages[requestid] = new List<JMSMessage>();
                        }

                        Messages[requestid].Add(jmsmsg);
                    }
                }
            }
            catch (Exception ex)
            {
                log.Info(String.Format("Invalid message received. {0}", ex.ToString()));
            }
        }
    }
}
